package com.minelx.util.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.stream.Stream;

public class Clock {
	private final Calendar self;

	private Clock(Calendar self) {
		this.self = self;
	}

	Stream<Clock> offsets(ClockUnit clockUnit, int amount) {
		return Stream.iterate(this,
				each -> each.offset(clockUnit, amount));
	}

	String format(String pattern) {
		return new SimpleDateFormat(pattern)
				.format(self.getTime());
	}

	public Clock offset(ClockUnit day, int amount) {
		Calendar copied = copyCalendar(self);
		day.add(copied, amount);
		return new Clock(copied);
	}

	public Clock locate(ClockUnit month, int at) {
		Calendar copied = copyCalendar(self);
		month.set(copied, at);
		return new Clock(copied);
	}

	public int year() {
		return self.get(Calendar.YEAR);
	}

	public int month() {
		return self.get(Calendar.MONTH) + 1;
	}

	public int day() {
		return self.get(Calendar.DAY_OF_MONTH);
	}

	public String represent() {
		return String.format(
				"ClockAt{%d, %d, %d, %d, %d, %d, %d}",
				self.get(Calendar.YEAR),
				self.get(Calendar.MONTH) + 1,
				self.get(Calendar.DAY_OF_MONTH),
				self.get(Calendar.HOUR_OF_DAY),
				self.get(Calendar.MINUTE),
				self.get(Calendar.SECOND),
				self.get(Calendar.MILLISECOND));
	}

	Calendar self() {
		return self;
	}

	// FIXME wait for:  extract literal 'Month'
	public Clock convertPreciseToMonth() {
		return Clock.of(year(), month(), 1);
	}

	public static Clock of(int year, int month, int day) {
		Calendar result = Calendar.getInstance();
		//noinspection MagicConstant
		result.set(year, month - 1, day, 0, 0, 0);
		result.set(Calendar.MILLISECOND, 0);
		return new Clock(result);
	}

	public static Clock from(Calendar calendar) {
		return new Clock(copyCalendar(calendar));
	}

	static Clock from(String text) {
		Calendar result = Calendar.getInstance();
		result.setTime(new Date(parse(text)));
		return new Clock(result);
	}

	private static Calendar copyCalendar(Calendar source) {
		Calendar result = Calendar.getInstance();
		result.set(source.get(Calendar.YEAR),
				source.get(Calendar.MONTH),
				source.get(Calendar.DAY_OF_MONTH),
				0,
				0,
				0);
		result.set(Calendar.MILLISECOND, 0);
		return result;
	}

	private static long parse(String time) {
		try {
			return new SimpleDateFormat("yyyyMMdd").parse(time).getTime();
		} catch (ParseException e) {
			throw new RuntimeException("something is wrong here", e);
		}
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		Clock clock = (Clock) o;
		return self.equals(clock.self);
	}

	@Override
	public int hashCode() {
		return Objects.hash(self);
	}
}
